
const OpenAI = require("openai");
const config  = require('./config.js')
const hx = require("hbuilderx");

const openai = new OpenAI({
    apiKey: config.getData('apiKey'),
    baseURL: config.getData('baseUrl'),
    dangerouslyAllowBrowser:true
});
let outputChannel = hx.window.createOutputChannel("ucoder");
var abortController = null

async function getResponse(messages) {
	// 取消之前的请求
	    if (abortController) {
			console.log('取消上次请求')
	        abortController.abort();
	    }
    abortController = new AbortController();
    try {
		openai.apiKey = config.getData('apiKey')
		openai.baseURL = config.getData('baseUrl')
		
		hx.window.setStatusBarMessage('ucoder正在获取建议',0,'info');
		
		console.log(config.getData('modelName'))
		
        const completion = await openai.chat.completions.create({
            model: config.getData('modelName'),
            messages: messages,
            stream: false,
            // stream_options: {
            //     include_usage: true
            // },
            enable_thinking: false,
            max_tokens:4096
        }, {
            signal: abortController.signal // 传入中断信号
        });
        return completion.choices[0].message.content;
    } catch (error) {
		// 如果是主动取消的请求，不抛出错误
		if (isAbortError(error)) {
			console.log('Request was aborted');
			return null;
		}
		outputChannel.show();
		outputChannel.appendLine(JSON.stringify(error));
        console.error("Error fetching response:", error);
        throw error;  // 重新抛出异常以便上层处理
    }finally {
		hx.window.clearStatusBarMessage();
        // 请求完成后清理 abortController
        abortController = null;
    }
}

function generateCode (document, position){
	
	return new Promise(async (resolve) => {

		let prompt = buildGenerateCodePrompt(document, position)
		const response = await getResponse([
			  {"role": "system", "content": prompt}
			])
		const newRes = getInsertionTextWithNewline(response,document, position)	
		resolve(newRes)

	})
	
}


function buildGenerateCodePrompt(document, position){
	const language = document.languageId;
	const fileName = document.fileName;
	const projectType = document.workspaceFolder ? document.workspaceFolder.nature : 'unknown';
	const projectName = document.workspaceFolder ? document.workspaceFolder.name : 'unknown';
	const {beforeCursor,afterCursor} = getTextAroundCursor(document, position)
	// 基础设定
	let prompt = `# 角色
					你是一个${language}代码专家，任务是完成部分编写的代码。
	
					# 任务
					1. 分析标记为「CURSOR」位置的代码上下文
					2. 生成最可能的下文代码
					3. 输出必须直接是可插入的代码，不要额外说明
					4. 如果没有可用的代码或建议，直接回复空字符串
	
					# 规则
					- 保持完全一致的缩进和代码风格
					- 如果正在编写函数/方法，确保补全所有必要参数
					- 如果明显是未完成的控制结构，补全相应语法
					- 不要重复已有代码
					/** 代码续写任务 **/
					  项目类型:${projectType}
					  项目名称:${projectName}
					  文件语言: ${language}
					  文件名称: ${fileName}  
					  /** 上下文 **/
					  已有代码:
					  
					  ${beforeCursor}「CURSOR」${afterCursor}
					  
					  # 规则
					  请直接续写「CURSOR」位置的代码，要求:
					  1. 不要包含已有代码
					  2. 保持完全相同的缩进和风格
					  3. 不要添加任何解释
					  4. 回复可以直接使用的代码，不要类似'javascript'开头的语言类型说明
					  5. 如果是独立的代码块需要以换行开头
					  `

	return prompt
}


function getInsertionTextWithNewline(output,document, position) {
	const context = getEditorContext(document, position);
	const needsNewline = shouldAddNewlineBeforeInsert(context);
	return needsNewline ? '\n' + output : output;
}

// 判断是否应该以换行开始
function shouldAddNewlineBeforeInsert(context) {
  const { prevLineText,currentPrefix } = context;
  
  // 如果前缀是未闭合的块（如 {, [）
  if (/[{\[]\s*$/.test(currentPrefix)) return true;
  
  // 如果前缀是闭合符号或完整语句（如 ;,}, ), ]）
  if (/^\s*[;}\])]/.test(currentPrefix)) return true;
  
  // 如果上一行是完整语句（以;或}结尾）
  if (/[;}\])]$/.test(prevLineText)) return true;
  
  // 当前行是注释或空行
  if (/^\s*(\/\/|\/\*|\*)/.test(currentPrefix) || currentPrefix.trim() === '') return true;
  
  return false;
}

function getEditorContext(document, position) {

	// 获取光标位置
  const cursorLine = position.line;
  let prevLineText = ''
  // 获取当前行文本
  const currentLineText = document.lineAt(cursorLine).text;
  // 向上查找非空行的上一行（最多查5行）
  let linesToCheck = 1;
  while (prevLineText.trim() === '' && cursorLine - linesToCheck >= 0 && linesToCheck <=5) {
	  prevLineText = document.lineAt(cursorLine - linesToCheck).text;
		linesToCheck++;
  }

  return {
    prevLineText:prevLineText,
    currentPrefix: currentLineText
  };
}


function getTextAroundCursor(
  document,
  position,
  linesAround = 10
){
	linesAround = config.getData('codeLine')
	// 获取光标前的代码		
  let beforeCursor=''
  if(linesAround==0){
	  beforeCursor = document.getText(
	    new hx.Range(new hx.Position(0, 0), position),
	  );
  }else{
	  const startLine = Math.max(0, position.line - linesAround);
	  const firstLineStart = document.lineAt(startLine).range.start;
	  beforeCursor = document.getText(
	    new hx.Range(firstLineStart, position),
	  );
  }
  // 获取光标后的代码
  let lastLine = document.lineCount - 1;
  let afterCursor = ''
  let lastLineEnd = null
  if(linesAround==0){
	  lastLineEnd = document.lineAt(lastLine).range.end;
	  afterCursor = document.getText(new hx.Range(position, lastLineEnd));
  }else{
	  const endLine = Math.min(document.lineCount - 1, position.line + linesAround);
	  lastLineEnd = document.lineAt(endLine).range.end;
	  afterCursor = document.getText(new hx.Range(position, lastLineEnd));
  }

	const MAX_TOTAL_LENGTH = 3600;
	
	// 确保MAX_TOTAL_LENGTH是有效正数
	const maxLength = Math.max(0, Number(MAX_TOTAL_LENGTH) || 3600);
	
	// 转换为字符串（如果输入不是字符串）
	beforeCursor = String(beforeCursor || '');
	afterCursor = String(afterCursor || '');
	
	if (beforeCursor.length + afterCursor.length > maxLength) {
	    const totalToKeep = maxLength;
	    const keepBeforeRatio = 0.7; // 70%给beforeCursor，30%给afterCursor
	    
	    // 按比例分配保留长度
	    const keepBefore = Math.min(
	        beforeCursor.length,
	        Math.floor(totalToKeep * keepBeforeRatio)
	    );
	    const keepAfter = Math.min(
	        afterCursor.length,
	        totalToKeep - keepBefore
	    );
	    
	    beforeCursor = beforeCursor.slice(-keepBefore);
	    afterCursor = afterCursor.slice(0, keepAfter);
	}
  
  
  return { beforeCursor, afterCursor };
}


function isAbortError(error) {
    return (
        error.name === 'AbortError' || // 标准 AbortError
        error.name === 'APIUserAbortError' || // 你的案例中的错误
        error.message?.includes('aborted') || // 检查错误消息
        error.code === 'ECONNABORTED' // 某些环境下的网络中止错误
    );
}

module.exports = {
	generateCode
}

